/*
 * THE FOLLOWING FIRMWARE IS PROVIDED: (1) "AS IS" WITH NO WARRANTY; AND 
 * (2)TO ENABLE ACCESS TO CODING INFORMATION TO GUIDE AND FACILITATE CUSTOMER.
 * CONSEQUENTLY, SEMTECH SHALL NOT BE HELD LIABLE FOR ANY DIRECT, INDIRECT OR
 * CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE CONTENT
 * OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING INFORMATION
 * CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
 * 
 * Copyright (C) SEMTECH S.A.
 */
/*! 
 * \file       main.c
 * \brief              
 * \version    1.0
 * \date       
 * \author     
 */
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */

/* fix an issue between POSIX and C99 */
#if __STDC_VERSION__ >= 199901L
    #define _XOPEN_SOURCE 600
#else
    #define _XOPEN_SOURCE 500
#endif
 
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>         /* memset */
#include <signal.h>         /* sigaction */
#include <time.h>           /* time, clock_gettime, strftime, gmtime */
#include <sys/time.h>       /* timeval */
#include <unistd.h>         /* getopt, access */
#include <stdlib.h>         /* atoi, exit */
#include <errno.h>          /* error messages */
#include <math.h>           /* modf */
#include <assert.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <getopt.h>     /* getopt_long */
#include <sys/stat.h>
#include <dlfcn.h>

#include "current.h"
#include "parson.h"
#include "semaphore.h"

#include "radio.h"
#include "loragw_hal.h"
#include "loragw_reg.h"
#include "radios_hal.h"
#include "transfer.h"
#include "gpio.h"

#define DEBUG_ARRAY(a,b,c)            for(a=0;a<b;++a) fprintf(stderr,"%x.",c[a]);fprintf(stderr,"end\n")
#define ARRAY_SIZE(a)   (sizeof(a) / sizeof((a)[0]))
#define STRINGIFY(x)    #x
#define STR(x)          STRINGIFY(x)

#define MSG(args...) printf(args) /* message that is destined to the user */
#define MSG_DEBUG(FLAG, fmt, ...)                                                                         \
            do  {                                                                                         \
                if (FLAG)                                                                                 \
                    fprintf(stdout, "%s:%d:%s(): " fmt, __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__); \
            } while (0)





typedef enum{
    PD_TEST_IDLE,
    PD_TEST_RADIO,
    PD_TEST_RADIO_DONE,
    PD_TEST_RECV_START,
    PD_TEST_RECV_DONE,
    PD_TEST_SEND_START,
    PD_TEST_SEND_DONE,
    PD_TEST_CURRENT,
    
    PD_TEST_DONE
}pd_test_t;


typedef struct{
    /* radio 0/1 freq */
    uint32_t radio0_freq;
    bool radio0_result;
    uint32_t radio1_freq;
    bool radio1_result;
    /* module recv test result */
    uint32_t recv_freq;
    float recv_current; 
    int16_t recv_rssi;
    int8_t recv_snr;
    bool recv_result;
    /* module send test result */
    uint32_t send_freq;
    float send_current; 
    int16_t send_rssi;
    int8_t send_snr;
    bool send_result;
}pd_test_result_t;
/*
 * Manages the master operation
 */



/*
 * Main application entry point.
 */
rfPara_t g_tRfPara = {
        .ulFreq = 472700000,
        .ulFdev = 0,
        .bandwidthAfc = 0,
        .cPwr = 20,
        .ucBw = 7,//125k
        .ucSf = 7,//sf7
        .ucCodingRate = 1,
        .usPreamble = 8,
        .usWakeTime = 100,
        .iqInverted = 0,
        .ucCrc = 1,
        .ucFhss = 0,
        .ucModem = 1
};


int count = 0;
static pd_test_t steps = PD_TEST_IDLE;
static pd_test_t last_steps = PD_TEST_IDLE;

static sem_t pdsem;

static char *lib_dll_md134x = "/usr/local/lib/libmd134x.so";
static char *lib_dll_sx1301 = "/usr/local/lib/libsx1301.so";
static char *lib_dll_sx1276 = "/usr/local/lib/libsx1276.so";

/* signal handling variables */
volatile bool exit_sig = false; /* 1 -> application terminates cleanly (shut down hardware, close open files, etc) */
volatile bool quit_sig = false; /* 1 -> application terminates without shutting down the hardware */
volatile bool test_done = false;
/* Gateway specificities */
static int8_t antenna_gain = 0;

/* TX capabilities */
static struct lgw_tx_gain_lut_s txlut; /* TX gain table */
static uint32_t tx_freq_min[LGW_RF_CHAIN_NB]; /* lowest frequency supported by TX chain */
static uint32_t tx_freq_max[LGW_RF_CHAIN_NB]; /* highest frequency supported by TX chain */


float send_snr_min = -5, send_rssi_min = -60, send_current_max = 700, send_current_min = 300,
	  receive_snr_min = -5, receive_rssi_min = -60, receive_current_max = 700, receive_current_min = 300;
char pd_id_head[8];
char pd_id_body[32];

/* sx1301
   0: standard chip
   1: to be tested chip*/
uint32_t tx_test_freq[2];
uint32_t rx_test_freq[2];
uint32_t center_freq[2];
int32_t if_freq[LGW_MULTI_NB][2];
uint32_t radio_freq[2];

#define RESULT_SIZE         350
#define FETCH_SLEEP_MS      10
#define TX_FREQ_MIN 429000000
#define TX_FREQ_MAX 928000000

#define PAYLOAD_SIZE	40

uint8_t PD_HEAD_LEN;
uint8_t PD_TOTAL_LEN;

#define AD_COM_PATH_DEFAULT  "/dev/ttyS5"

char ad_com_path_default[16] = AD_COM_PATH_DEFAULT;
void *current_com_target;
pthread_t pthread_radios;
pthread_t pthread_module_recv;
pthread_t pthread_standard_recv;
sem_t sem_radio[2];//, sem_radio1;
sem_t sem_recv;//, sem_recv;
sem_t sem_send;//, sem_send;
uint32_t time_on_air = 0;
pd_test_result_t result = { 0 };
bool use_md134x = false;
/* -------------------------------------------------------------------------- */
/* --- PRIVATE FUNCTIONS DECLARATION ---------------------------------------- */

static void sig_handler(int sigio);
static void exit_handler(void);
static int parse_SX1301_configuration(const char * conf_file);
static int parse_fac_test_configuration(const char * conf_file) ;

static int current_test(float *val);
static double difftimespec(struct timespec end, struct timespec beginning);

static void pd_load_configuration_files(char *global_path, char *factory_path);
//static void pd_load_configuration_files(void);
static void pd_input_product_id(void);
static int pd_check_product_id(char *id);
static int pd_test_radio(int radio);
static int pd_test_recv_start(void);
static int pd_test_recv_wait(void);
static int pd_test_send_start(void);
static int pd_test_send_wait(void);
static int pd_test_current(float *val);
static int pd_test_result_print(int fd);

static unsigned long pd_log_file_size(const char *path);
static void pd_test_result_log_file(char *in_file);
static void pd_test_module_reset_init(void);

static void pd_test_radios_recv_thread(void *arg);
static void pd_test_module_recv_thread(void *arg);
static void pd_standard_module_recv_thread(void *arg);

/* threads */



static void sig_handler(int sigio) {
    if (sigio == SIGQUIT) {
        quit_sig = true;
    } else if ((sigio == SIGINT) || (sigio == SIGTERM)) {
        exit_sig = true;
    }
    return;
}

static void exit_handler(void)
{
    pd_close_dll(use_md134x);
}

static int parse_SX1301_configuration(const char * conf_file) {
    int i;
    uint8_t index;
    bool freq_check_ok = false;
    char param_name[32]; /* used to generate variable parameter names */
    const char *str; /* used to store string value from JSON object */
    const char conf_obj_name[] = "SX1301_conf";
    JSON_Value *root_val = NULL;
    JSON_Object *conf_obj = NULL;
    JSON_Value *val = NULL;
    struct lgw_conf_board_s boardconf;
    struct lgw_conf_rxrf_s rfconf;
    struct lgw_conf_rxif_s ifconf;
    uint32_t sf, bw, fdev;

    /* try to parse JSON */
    root_val = json_parse_file_with_comments(conf_file);
    if (root_val == NULL) {
        MSG("ERROR: %s is not a valid JSON file\n", conf_file);
        exit(EXIT_FAILURE);
    }

    /* point to the gateway configuration object */
    conf_obj = json_object_get_object(json_value_get_object(root_val), conf_obj_name);
    if (conf_obj == NULL) {
        MSG("INFO: %s does not contain a JSON object named %s\n", conf_file, conf_obj_name);
        return -1;
    } else {
        MSG("INFO: %s does contain a JSON object named %s, parsing SX1301 parameters\n", conf_file, conf_obj_name);
    }

    /* set board configuration */
    memset(&boardconf, 0, sizeof boardconf); /* initialize configuration structure */
    val = json_object_get_value(conf_obj, "lorawan_public"); /* fetch value (if possible) */
    if (json_value_get_type(val) == JSONBoolean) {
        boardconf.lorawan_public = (bool)json_value_get_boolean(val);
    } else {
        MSG("WARNING: Data type for lorawan_public seems wrong, please check\n");
        boardconf.lorawan_public = false;
    }
    val = json_object_get_value(conf_obj, "clksrc"); /* fetch value (if possible) */
    if (json_value_get_type(val) == JSONNumber) {
        boardconf.clksrc = (uint8_t)json_value_get_number(val);
    } else {
        MSG("WARNING: Data type for clksrc seems wrong, please check\n");
        boardconf.clksrc = 0;
    }
    MSG("INFO: lorawan_public %d, clksrc %d\n", boardconf.lorawan_public, boardconf.clksrc);
    /* all parameters parsed, submitting configuration to the HAL */
    if (pd_lgw_board_setconf(use_md134x, boardconf) != LGW_HAL_SUCCESS) {
        MSG("WARNING: Failed to configure board\n");
    }

    /* set antenna gain configuration */
    val = json_object_get_value(conf_obj, "antenna_gain"); /* fetch value (if possible) */
    if (json_value_get_type(val) == JSONNumber) {
        antenna_gain = (int8_t)json_value_get_number(val);
    } else {
        MSG("WARNING: Data type for antenna_gain seems wrong, please check\n");
        antenna_gain = 0;
    }
    MSG("INFO: antenna_gain %d dBi\n", antenna_gain);

    /* set configuration for tx gains */
    memset(&txlut, 0, sizeof txlut); /* initialize configuration structure */
    for (i = 0; i < TX_GAIN_LUT_SIZE_MAX; i++) {
        snprintf(param_name, sizeof param_name, "tx_lut_%i", i); /* compose parameter path inside JSON structure */
        val = json_object_get_value(conf_obj, param_name); /* fetch value (if possible) */
        if (json_value_get_type(val) != JSONObject) {
            MSG("INFO: no configuration for tx gain lut %i\n", i);
            continue;
        }
        txlut.size++; /* update TX LUT size based on JSON object found in configuration file */
        /* there is an object to configure that TX gain index, let's parse it */
        snprintf(param_name, sizeof param_name, "tx_lut_%i.pa_gain", i);
        val = json_object_dotget_value(conf_obj, param_name);
        if (json_value_get_type(val) == JSONNumber) {
            txlut.lut[i].pa_gain = (uint8_t)json_value_get_number(val);
        } else {
            MSG("WARNING: Data type for %s[%d] seems wrong, please check\n", param_name, i);
            txlut.lut[i].pa_gain = 0;
        }
                snprintf(param_name, sizeof param_name, "tx_lut_%i.dac_gain", i);
                val = json_object_dotget_value(conf_obj, param_name);
                if (json_value_get_type(val) == JSONNumber) {
                        txlut.lut[i].dac_gain = (uint8_t)json_value_get_number(val);
                } else {
                        txlut.lut[i].dac_gain = 3; /* This is the only dac_gain supported for now */
                }
                snprintf(param_name, sizeof param_name, "tx_lut_%i.dig_gain", i);
                val = json_object_dotget_value(conf_obj, param_name);
                if (json_value_get_type(val) == JSONNumber) {
                        txlut.lut[i].dig_gain = (uint8_t)json_value_get_number(val);
                } else {
            MSG("WARNING: Data type for %s[%d] seems wrong, please check\n", param_name, i);
                        txlut.lut[i].dig_gain = 0;
                }
                snprintf(param_name, sizeof param_name, "tx_lut_%i.mix_gain", i);
                val = json_object_dotget_value(conf_obj, param_name);
                if (json_value_get_type(val) == JSONNumber) {
                        txlut.lut[i].mix_gain = (uint8_t)json_value_get_number(val);
                } else {
            MSG("WARNING: Data type for %s[%d] seems wrong, please check\n", param_name, i);
                        txlut.lut[i].mix_gain = 0;
                }
                snprintf(param_name, sizeof param_name, "tx_lut_%i.rf_power", i);
                val = json_object_dotget_value(conf_obj, param_name);
                if (json_value_get_type(val) == JSONNumber) {
                        txlut.lut[i].rf_power = (int8_t)json_value_get_number(val);
                } else {
            MSG("WARNING: Data type for %s[%d] seems wrong, please check\n", param_name, i);
                        txlut.lut[i].rf_power = 0;
                }
    }
    /* all parameters parsed, submitting configuration to the HAL */
    MSG("INFO: Configuring TX LUT with %u indexes\n", txlut.size);
        if (pd_lgw_txgain_setconf(use_md134x, &txlut) != LGW_HAL_SUCCESS) {
                MSG("WARNING: Failed to configure concentrator TX Gain LUT\n");
    }

    /* set configuration for RF chains */
    for (i = 0; i < LGW_RF_CHAIN_NB; ++i) {
        memset(&rfconf, 0, sizeof rfconf); /* initialize configuration structure */
        snprintf(param_name, sizeof param_name, "radio_%i", i); /* compose parameter path inside JSON structure */
        val = json_object_get_value(conf_obj, param_name); /* fetch value (if possible) */
        if (json_value_get_type(val) != JSONObject) {
            MSG("INFO: no configuration for radio %i\n", i);
            continue;
        }
        /* there is an object to configure that radio, let's parse it */
        snprintf(param_name, sizeof param_name, "radio_%i.enable", i);
        val = json_object_dotget_value(conf_obj, param_name);
        if (json_value_get_type(val) == JSONBoolean) {
            rfconf.enable = (bool)json_value_get_boolean(val);
        } else {
            rfconf.enable = false;
        }
        if (rfconf.enable == false) { /* radio disabled, nothing else to parse */
            MSG("INFO: radio %i disabled\n", i);
        } else  { /* radio enabled, will parse the other parameters */
            snprintf(param_name, sizeof param_name, "radio_%i.freq", i);
            rfconf.freq_hz = (uint32_t)json_object_dotget_number(conf_obj, param_name);
            center_freq[i] = rfconf.freq_hz;
            snprintf(param_name, sizeof param_name, "radio_%i.rssi_offset", i);
            rfconf.rssi_offset = (float)json_object_dotget_number(conf_obj, param_name);
            snprintf(param_name, sizeof param_name, "radio_%i.type", i);
            str = json_object_dotget_string(conf_obj, param_name);
            if (!strncmp(str, "SX1255", 6)) {
                rfconf.type = LGW_RADIO_TYPE_SX1255;
            } else if (!strncmp(str, "SX1257", 6)) {
                rfconf.type = LGW_RADIO_TYPE_SX1257;
            } else {
                MSG("WARNING: invalid radio type: %s (should be SX1255 or SX1257)\n", str);
            }
            snprintf(param_name, sizeof param_name, "radio_%i.tx_enable", i);
            val = json_object_dotget_value(conf_obj, param_name);
            if (json_value_get_type(val) == JSONBoolean) {
                rfconf.tx_enable = (bool)json_value_get_boolean(val);
                if (rfconf.tx_enable == true) {
                    /* tx is enabled on this rf chain, we need its frequency range */
                    snprintf(param_name, sizeof param_name, "radio_%i.tx_freq_min", i);
                    tx_freq_min[i] = (uint32_t)json_object_dotget_number(conf_obj, param_name);
                    snprintf(param_name, sizeof param_name, "radio_%i.tx_freq_max", i);
                    tx_freq_max[i] = (uint32_t)json_object_dotget_number(conf_obj, param_name);
                    if ((tx_freq_min[i] == 0) || (tx_freq_max[i] == 0)) {
                        MSG("WARNING: no frequency range specified for TX rf chain %d\n", i);
                    }
                }
            } else {
                rfconf.tx_enable = false;
            }
            MSG("INFO: radio %i enabled (type %s), center frequency %u, RSSI offset %f, tx enabled %d\n", i, str, rfconf.freq_hz, rfconf.rssi_offset, rfconf.tx_enable);
        }
        /* all parameters parsed, submitting configuration to the HAL */
        if (pd_lgw_rxrf_setconf(use_md134x, i, rfconf) != LGW_HAL_SUCCESS) {
            MSG("WARNING: invalid configuration for radio %i\n", i);
        }
    }
    
    /* set configuration for Lora multi-SF channels (bandwidth cannot be set) */
    for (i = 0; i < LGW_MULTI_NB; ++i) {
        memset(&ifconf, 0, sizeof ifconf); /* initialize configuration structure */
        snprintf(param_name, sizeof param_name, "chan_multiSF_%i", i); /* compose parameter path inside JSON structure */
        val = json_object_get_value(conf_obj, param_name); /* fetch value (if possible) */
        if (json_value_get_type(val) != JSONObject) {
            MSG("INFO: no configuration for Lora multi-SF channel %i\n", i);
            continue;
        }
        /* there is an object to configure that Lora multi-SF channel, let's parse it */
        snprintf(param_name, sizeof param_name, "chan_multiSF_%i.enable", i);
        val = json_object_dotget_value(conf_obj, param_name);
        if (json_value_get_type(val) == JSONBoolean) {
            ifconf.enable = (bool)json_value_get_boolean(val);
        } else {
            ifconf.enable = false;
        }
        if (ifconf.enable == false) { /* Lora multi-SF channel disabled, nothing else to parse */
            MSG("INFO: Lora multi-SF channel %i disabled\n", i);
        } else  { /* Lora multi-SF channel enabled, will parse the other parameters */
            snprintf(param_name, sizeof param_name, "chan_multiSF_%i.radio", i);
            ifconf.rf_chain = (uint32_t)json_object_dotget_number(conf_obj, param_name);
            if_freq[i][0] = ifconf.rf_chain;
            snprintf(param_name, sizeof param_name, "chan_multiSF_%i.if", i);
            ifconf.freq_hz = (int32_t)json_object_dotget_number(conf_obj, param_name);
            if_freq[i][1] = ifconf.freq_hz;
            //  TODO: handle individual SF enabling and disabling (spread_factor)
            MSG("INFO: Lora multi-SF channel %i>  radio %i, IF %i Hz, 125 kHz bw, SF 7 to 12\n", i, ifconf.rf_chain, ifconf.freq_hz);
        }
        /* all parameters parsed, submitting configuration to the HAL */
        if (pd_lgw_rxif_setconf(use_md134x, i, ifconf) != LGW_HAL_SUCCESS) {
            MSG("WARNING: invalid configuration for Lora multi-SF channel %i\n", i);
        }
    }

    radio_freq[0] = 0;
    radio_freq[1] = 0;
    /* check rf freq configurations */
    for(index = 0; index < 8; index++){
        if(radio_freq[if_freq[index][0]] == 0){
            radio_freq[if_freq[index][0]] = (center_freq[if_freq[index][0]] + if_freq[index][1]);
        }
        
        if(rx_test_freq[0] == (center_freq[if_freq[index][0]] + if_freq[index][1])){
            freq_check_ok = true;
            //break;
        }
    }
    if(!freq_check_ok){
        printf("ERROR: global_conf.json or config.json RX Frequency wrong configuration. \n");
        exit(EXIT_FAILURE);
    }
    printf("##radio 0: %u .\n", radio_freq[0]);
    printf("##radio 1: %u .\n", radio_freq[1]);
    /* set configuration for Lora standard channel */
    memset(&ifconf, 0, sizeof ifconf); /* initialize configuration structure */
    val = json_object_get_value(conf_obj, "chan_Lora_std"); /* fetch value (if possible) */
    if (json_value_get_type(val) != JSONObject) {
        MSG("INFO: no configuration for Lora standard channel\n");
    } else {
        val = json_object_dotget_value(conf_obj, "chan_Lora_std.enable");
        if (json_value_get_type(val) == JSONBoolean) {
            ifconf.enable = (bool)json_value_get_boolean(val);
        } else {
            ifconf.enable = false;
        }
        if (ifconf.enable == false) {
            MSG("INFO: Lora standard channel %i disabled\n", i);
        } else  {
            ifconf.rf_chain = (uint32_t)json_object_dotget_number(conf_obj, "chan_Lora_std.radio");
            ifconf.freq_hz = (int32_t)json_object_dotget_number(conf_obj, "chan_Lora_std.if");
            bw = (uint32_t)json_object_dotget_number(conf_obj, "chan_Lora_std.bandwidth");
            switch(bw) {
                case 500000: ifconf.bandwidth = BW_500KHZ; break;
                case 250000: ifconf.bandwidth = BW_250KHZ; break;
                case 125000: ifconf.bandwidth = BW_125KHZ; break;
                default: ifconf.bandwidth = BW_UNDEFINED;
            }
            sf = (uint32_t)json_object_dotget_number(conf_obj, "chan_Lora_std.spread_factor");
            switch(sf) {
                case  7: ifconf.datarate = DR_LORA_SF7;  break;
                case  8: ifconf.datarate = DR_LORA_SF8;  break;
                case  9: ifconf.datarate = DR_LORA_SF9;  break;
                case 10: ifconf.datarate = DR_LORA_SF10; break;
                case 11: ifconf.datarate = DR_LORA_SF11; break;
                case 12: ifconf.datarate = DR_LORA_SF12; break;
                default: ifconf.datarate = DR_UNDEFINED;
            }
            MSG("INFO: Lora std channel> radio %i, IF %i Hz, %u Hz bw, SF %u\n", ifconf.rf_chain, ifconf.freq_hz, bw, sf);
        }
        if (pd_lgw_rxif_setconf(use_md134x, 8, ifconf) != LGW_HAL_SUCCESS) {
            MSG("WARNING: invalid configuration for Lora standard channel\n");
        }
    }

    /* set configuration for FSK channel */
    memset(&ifconf, 0, sizeof ifconf); /* initialize configuration structure */
    val = json_object_get_value(conf_obj, "chan_FSK"); /* fetch value (if possible) */
    if (json_value_get_type(val) != JSONObject) {
        MSG("INFO: no configuration for FSK channel\n");
    } else {
        val = json_object_dotget_value(conf_obj, "chan_FSK.enable");
        if (json_value_get_type(val) == JSONBoolean) {
            ifconf.enable = (bool)json_value_get_boolean(val);
        } else {
            ifconf.enable = false;
        }
        if (ifconf.enable == false) {
            MSG("INFO: FSK channel %i disabled\n", i);
        } else  {
            ifconf.rf_chain = (uint32_t)json_object_dotget_number(conf_obj, "chan_FSK.radio");
            ifconf.freq_hz = (int32_t)json_object_dotget_number(conf_obj, "chan_FSK.if");
            bw = (uint32_t)json_object_dotget_number(conf_obj, "chan_FSK.bandwidth");
            fdev = (uint32_t)json_object_dotget_number(conf_obj, "chan_FSK.freq_deviation");
            ifconf.datarate = (uint32_t)json_object_dotget_number(conf_obj, "chan_FSK.datarate");

            /* if chan_FSK.bandwidth is set, it has priority over chan_FSK.freq_deviation */
            if ((bw == 0) && (fdev != 0)) {
                bw = 2 * fdev + ifconf.datarate;
            }
            if      (bw == 0)      ifconf.bandwidth = BW_UNDEFINED;
            else if (bw <= 7800)   ifconf.bandwidth = BW_7K8HZ;
            else if (bw <= 15600)  ifconf.bandwidth = BW_15K6HZ;
            else if (bw <= 31200)  ifconf.bandwidth = BW_31K2HZ;
            else if (bw <= 62500)  ifconf.bandwidth = BW_62K5HZ;
            else if (bw <= 125000) ifconf.bandwidth = BW_125KHZ;
            else if (bw <= 250000) ifconf.bandwidth = BW_250KHZ;
            else if (bw <= 500000) ifconf.bandwidth = BW_500KHZ;
            else ifconf.bandwidth = BW_UNDEFINED;

            MSG("INFO: FSK channel> radio %i, IF %i Hz, %u Hz bw, %u bps datarate\n", ifconf.rf_chain, ifconf.freq_hz, bw, ifconf.datarate);
        }
        if (pd_lgw_rxif_setconf(use_md134x, 9, ifconf) != LGW_HAL_SUCCESS) {
            MSG("WARNING: invalid configuration for FSK channel\n");
        }
    }
    json_value_free(root_val);
    return 0;
}


static int parse_fac_test_configuration(const char * conf_file) {
    uint8_t pwr = 0;
	char conf_obj_name[16];
    JSON_Value *root_val;
    JSON_Object *conf_obj = NULL;
    JSON_Value *val = NULL; /* needed to detect the absence of some fields */
    const char *str; /* pointer to sub-strings in the JSON data */
    unsigned long long ull = 0;

    /* try to parse JSON */
    root_val = json_parse_file_with_comments(conf_file);
    if (root_val == NULL) {
        printf("ERROR: %s is not a valid JSON file\n", conf_file);
		return -1;
    }

    /* point to the gateway configuration object */
	memset(conf_obj_name, 0, sizeof(conf_obj_name));
	memcpy(conf_obj_name, "thresholds", strlen("thresholds"));
    conf_obj = json_object_get_object(json_value_get_object(root_val), conf_obj_name);
    if (conf_obj == NULL) {
        printf("INFO: %s does not contain a JSON object named %s\n", conf_file, conf_obj_name);
		json_value_free(root_val);
        return -1;
    } else {
        MSG("INFO: %s contains a JSON object named %s, parsing gateway parameters\n", 
                conf_file, conf_obj_name);
    }

    val = json_object_get_value(conf_obj, "send_snr_min");
    if (val != NULL) {
		send_snr_min = (float)json_value_get_number(val);
        MSG("send_snr_min is configured to %f\n", send_snr_min);
    }

    val = json_object_get_value(conf_obj, "send_rssi_min");
    if (val != NULL) {
		send_rssi_min = (float)json_value_get_number(val);
        MSG("send_rssi_min is configured to %f\n", send_rssi_min);
    }

	val = json_object_get_value(conf_obj, "send_current_max");
    if (val != NULL) {
		send_current_max = (float)json_value_get_number(val);
        MSG("send_current_max is configured to %f\n", send_current_max);
    }

    val = json_object_get_value(conf_obj, "send_current_min");
    if (val != NULL) {
		send_current_min = (float)json_value_get_number(val);
        MSG("send_current_min is configured to %f\n", send_current_min);
    }

	val = json_object_get_value(conf_obj, "receive_snr_min");
    if (val != NULL) {
		receive_snr_min = (float)json_value_get_number(val);
        MSG("receive_snr_min is configured to %f\n", receive_snr_min);
    }

    val = json_object_get_value(conf_obj, "receive_rssi_min");
    if (val != NULL) {
		receive_rssi_min = (float)json_value_get_number(val);
        MSG("receive_rssi_min is configured to %f\n", receive_rssi_min);
    }

    val = json_object_get_value(conf_obj, "receive_current_max");
    if (val != NULL) {
		receive_current_max = (float)json_value_get_number(val);
        MSG("receive_current_max is configured to %f\n", receive_current_max);
    }

    val = json_object_get_value(conf_obj, "receive_current_min");
    if (val != NULL) {
		receive_current_min = (float)json_value_get_number(val);
        MSG("receive_current_min is configured to %f\n", receive_current_min);
    }

	memset(conf_obj_name, 0, sizeof(conf_obj_name));
	memcpy(conf_obj_name, "global", strlen("global"));
    conf_obj = json_object_get_object(json_value_get_object(root_val), conf_obj_name);
    if (conf_obj == NULL) {
        printf("INFO: %s does not contain a JSON object named %s\n", conf_file, conf_obj_name);
		json_value_free(root_val);
        return -1;
    } else {
        MSG("INFO: %s contains a JSON object named %s, parsing gateway parameters\n", 
                conf_file, conf_obj_name);
    }

    str = json_object_get_string(conf_obj, "production_id");
    if (str != NULL) {
	    PD_HEAD_LEN = (uint8_t)strlen(str);
        memset(pd_id_head, 0, sizeof(pd_id_head));
		strcpy(pd_id_head, str);
        printf("production_id is configured to %s, head-len=%u\n", pd_id_head, PD_HEAD_LEN);
    } else {
		fprintf(stderr, "%s does not contain a JSON object named production_id\n", conf_obj_name);
		return -1;
	}

    val = json_object_get_value(conf_obj, "production_idlen");
    if (val != NULL) {
		PD_TOTAL_LEN = (uint8_t)json_value_get_number(val);
        MSG("SX1301_ID_LENGTH is configured to %u\n", PD_TOTAL_LEN);
    }

    val = json_object_get_value(conf_obj, "rf_power");
    if (val != NULL) {
		pwr = (uint8_t)json_value_get_number(val);
        if(pwr <= 27){
            g_tRfPara.cPwr = pwr;
        }
        MSG("module rf power is configured to %udbm\n", g_tRfPara.cPwr);
    }

    memset(conf_obj_name, 0, sizeof(conf_obj_name));
    sprintf(conf_obj_name, "downstream_freq");
    val = json_object_get_value(conf_obj, conf_obj_name);
    if (val != NULL) {
        tx_test_freq[0] = (uint32_t)json_value_get_number(val);
        if (tx_test_freq[0] < TX_FREQ_MIN || tx_test_freq[0] > TX_FREQ_MAX) {
            fprintf(stderr, "downstream_freq: %u is not in %u ~ %u HZ\n", tx_test_freq[0], TX_FREQ_MIN, TX_FREQ_MAX);
            return -1;
        }
        MSG("downstream_freq is configured to %u\n", tx_test_freq[0]);
    } else {
        memset(conf_obj_name, 0, sizeof(conf_obj_name));
        sprintf(conf_obj_name, "downstream_freq1");
        val = json_object_get_value(conf_obj, conf_obj_name);
        if (val != NULL) {
            tx_test_freq[0] = (uint32_t)json_value_get_number(val);
            if (tx_test_freq[0] < TX_FREQ_MIN || tx_test_freq[0] > TX_FREQ_MAX) {
                fprintf(stderr, "downstream_freq: %u is not in %u ~ %u HZ\n", tx_test_freq[0], TX_FREQ_MIN, TX_FREQ_MAX);
                return -1;
            }
            MSG("downstream_freq is configured to %u\n", tx_test_freq[0]);
        } else {
            fprintf(stderr, "does not contain a JSON object named %s\n", conf_obj_name);
            return -1;
        }
    }
    

    memset(conf_obj_name, 0, sizeof(conf_obj_name));
    sprintf(conf_obj_name, "upstream_freq");
    val = json_object_get_value(conf_obj, conf_obj_name);
    if (val != NULL) {
        rx_test_freq[0] = (uint32_t)json_value_get_number(val);
        if (rx_test_freq[0] < TX_FREQ_MIN || rx_test_freq[0] > TX_FREQ_MAX) {
            fprintf(stderr, "upstream_freq: %u is not in %u ~ %u HZ\n", rx_test_freq[0], TX_FREQ_MIN, TX_FREQ_MAX);
            return -1;
        }
        MSG("upstream_freq is configured to %u\n", rx_test_freq[0]);
    } else {
        memset(conf_obj_name, 0, sizeof(conf_obj_name));
        sprintf(conf_obj_name, "upstream_freq1");
        val = json_object_get_value(conf_obj, conf_obj_name);
        if (val != NULL) {
            rx_test_freq[0] = (uint32_t)json_value_get_number(val);
            if (rx_test_freq[0] < TX_FREQ_MIN || rx_test_freq[0] > TX_FREQ_MAX) {
                fprintf(stderr, "upstream_freq: %u is not in %u ~ %u HZ\n", rx_test_freq[0], TX_FREQ_MIN, TX_FREQ_MAX);
                return -1;
            }
            MSG("upstream_freq is configured to %u\n", rx_test_freq[0]);
        } else {        
            fprintf(stderr, "does not contain a JSON object named %s\n", conf_obj_name);
            return -1;
        }
    }
   

    /* free JSON parsing data structure */
    json_value_free(root_val);
    return 0;
}


static int pd_check_product_id(char *id)
{
	int i;		
	
	if (strlen(id) != PD_TOTAL_LEN) {
		fprintf(stderr, "Your sx1301 id's length must be %d\n", PD_TOTAL_LEN);
		return -1;
	}

	if (memcmp(id, pd_id_head, PD_HEAD_LEN)) {
		fprintf(stderr, "Your production id is error\n");
		return -1;
	}
	
	for (i = 0; i < PD_TOTAL_LEN; i++) {
		if (!((id[i] >= '0' && id[i] <= '9') || (id[i] >= 'a' && id[i] <= 'z') || (id[i] >= 'A' && id[i] <= 'Z'))) {
			fprintf(stderr, "Your sx1301 id must be in 0~9 a~f A~F\n");
			return -1;
		}
	}

	return 0;
}

static void pd_input_product_id(void)
{
    int i;
    char sx1301_id[128];

	do{
		fprintf(stdout, "\nPlease input your sx1301 ID: ");
		memset(sx1301_id, 0, sizeof(sx1301_id));
		fscanf(stdin, "%s", sx1301_id);
		memcpy(pd_id_body, sx1301_id, PD_TOTAL_LEN);
		i = pd_check_product_id(sx1301_id);	
	}while (i < 0);
}


static int pd_test_current(float *val)
{
    int ret;
    float value = 0;
    int fd = *(int *)current_com_target;
    
    //ret = current_read_single(fd, &value);
    printf("current : %d. \n", fd);
    ret = hal_current_read_continuous(fd, 2, &value);
    if (ret < 0 ) {
        *val = 0;
        return -1;
    }
    
    *val = value*1000;
	
	return 0;
}

static void pd_load_configuration_files(char *global_path, char *factory_path)
{
    int i;
    char *global_cfg_path= "/home/factory_test/global_conf.json";
    char *factory_test_cfg_path = "/home/factory_test/config.json";
    
    if(factory_path == NULL){
     	if(access(factory_test_cfg_path, R_OK) != 0){
    		fprintf(stderr, "Find no factory test configuration file");
    		exit(EXIT_FAILURE);
    	}else{
    		i = parse_fac_test_configuration(factory_test_cfg_path);
    		if (i < 0) {
    			fprintf(stderr, "Failed to parse configuration file named %s\n", factory_test_cfg_path);
    			exit(EXIT_FAILURE);
    		}
    	}
    }else{
     	if(access(factory_path, R_OK) != 0){
    		fprintf(stderr, "Find no factory test configuration file");
    		exit(EXIT_FAILURE);
    	}else{
    		i = parse_fac_test_configuration(factory_path);
    		if (i < 0) {
    			fprintf(stderr, "Failed to parse configuration file named %s\n", factory_path);
    			exit(EXIT_FAILURE);
    		}
    	}     
    }
    if(global_path == NULL){
        /* load configuration files */
        if (access(global_cfg_path, R_OK) == 0) { /* if there is a global conf, parse it and then try to parse local conf  */
            printf("INFO: found global configuration file %s, parsing it. \n", global_cfg_path);
            parse_SX1301_configuration(global_cfg_path);
        } else {
            printf("ERROR: [main] failed to find any configuration file named %s. \n", global_cfg_path);
            exit(EXIT_FAILURE);
        } 
    }else{
        /* load configuration files */
        if (access(global_path, R_OK) == 0) { /* if there is a global conf, parse it and then try to parse local conf  */
            printf("INFO: found global configuration file %s, parsing it. \n", global_path);
            parse_SX1301_configuration(global_path);
        } else {
            printf("ERROR: [main] failed to find any configuration file named %s. \n", global_path);
            exit(EXIT_FAILURE);
        } 
    }
}

static double difftimespec(struct timespec end, struct timespec beginning) {
    double x;

    x = 1E-9 * (double)(end.tv_nsec - beginning.tv_nsec);
    x += (double)(end.tv_sec - beginning.tv_sec);

    return x;
}



static void pd_test_radios_recv_thread(void *arg)
{
    int i;
    int radio_idx;
    int index;
    
    /* clock and log rotation management */
    //int log_rotate_interval = 3600; /* by default, rotation every hour */
    int time_check = 0; /* variable used to limit the number of calls to time() function */

    /* allocate memory for packet fetching and processing */
    struct lgw_pkt_rx_s rxpkt[16]; /* array containing up to 16 inbound packets metadata */
    struct lgw_pkt_rx_s *p; /* pointer on a RX packet */
    int nb_pkt;

    /* local timestamp variables until we get accurate GPS time */
    struct timespec fetch_time;
    char fetch_timestamp[30];
    struct tm * x;
    char contents[]="easylinkin-test-radio";

    
    radio_idx = *(int *)arg;
    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
    printf("pd_test_radios_recv_thread %d entry.\n", radio_idx);
    /* main loop */
    while ((quit_sig != 1) && (exit_sig != 1)) {
        /* fetch packets */
        memset(rxpkt, 0, sizeof(rxpkt));
        nb_pkt = pd_lgw_receive(use_md134x, ARRAY_SIZE(rxpkt), rxpkt);
        if (nb_pkt == LGW_HAL_ERROR) {
            MSG("ERROR: failed packet fetch, exiting\n");
            return;
        } else if (nb_pkt == 0) {
            /* wait a short time if no packets */
            usleep(1000*FETCH_SLEEP_MS);
        } else {
            /* local timestamp generation until we get accurate GPS time */
            clock_gettime(CLOCK_REALTIME, &fetch_time);
            x = gmtime(&(fetch_time.tv_sec));
            sprintf(fetch_timestamp, "%04i-%02i-%02i %02i:%02i:%02i.%03liZ", (x->tm_year) + 1900, (x->tm_mon) + 1, x->tm_mday, x->tm_hour, x->tm_min, x->tm_sec, (fetch_time.tv_nsec) / 1000000); /* ISO 8601 format */
        }
        
        /* log packets */
        for (i = 0; i < nb_pkt; ++i) {
            p = &rxpkt[i];
            /* printf received package into stdout */
			if(p->status == STAT_CRC_OK){
				if(!memcmp(p->payload , contents, strlen(contents))){
                    if(!memcmp(&p->payload[strlen(contents)] , pd_id_body, PD_TOTAL_LEN)){
                        if(p->payload[strlen(contents)+ PD_TOTAL_LEN] == (0x30 + radio_idx)){
                            printf("INFO: Radio %d Received package +++ .\n", radio_idx);
                            DEBUG_ARRAY(index, p->size, p->payload);
                            if(radio_idx == 0){
                                result.radio0_result = true;
                            }else{
                                result.radio1_result = true;
                            }
                            sem_post(&sem_radio[radio_idx]);
                            pthread_exit(0);
                        }
                    }
				}
			}            
        }

        /* check time and rotate log file if necessary */
    }
}


void usage(void)
{
    printf("***Available options:***\n");
    printf(" -h          print this help\n");
    printf(" --help      print this help\n");
    printf(" --md134x    current test md134x moudle \n");
    printf(" --global    followed by global_conf_XXX.json [default : /home/factory_test/global_conf.json].\n");
    printf(" --threhold  followed by config_xxx.json    [default : /home/factory_test/config.json].\n");
    printf(" --result    followed by xxx.log              [default : /home/factory_test/test.log].\n");
}



  
unsigned long pd_log_file_size(const char *path)
{  
    unsigned long filesize = -1;      
    struct stat statbuff;
    
    if(stat(path, &statbuff) < 0){  
        return filesize;
    }else{  
        filesize = statbuff.st_size;  
    }
    
    return filesize;
}  





void pd_test_result_log_file(char *in_file)
{
    unsigned long f_size = 0;
    char buf[256];
    char tmp[256];
    uint32_t index;
        
    memset(buf, 0, sizeof(buf));
    memset(tmp, 0, sizeof(tmp));
    strcpy(buf, in_file);
    if(0 == access(in_file, F_OK)){
        if(pd_log_file_size(in_file) >= RESULT_SIZE*100){
            for(index = 1; index < 65536; index++){
                sprintf(tmp, "%s.%s", in_file, index);
                if(0 == access(in_file, F_OK)){
                    continue;
                }else{
                    sprintf(buf, "mv %s %s", in_file, tmp);
                    system(buf);
                    break;
                }
            }           
        }
    }
    
    //printf("log file size: %u.\n", pd_log_file_size(factory_test_log_path));
}


void pd_test_module_reset_init(void)
{
    if(use_md134x){
        gpio_export(51);
        gpio_set_direction(51, DIRECTION_OUTPUT);
        gpio_set_value(51, VALUE_LOW);
        usleep(50*1000);
        gpio_set_value(51, VALUE_HIGH);
        usleep(50*1000);
        
        gpio_export(60);
        gpio_set_direction(60, DIRECTION_OUTPUT);
        gpio_set_value(60, VALUE_LOW);
        usleep(100*1000);
        gpio_set_value(60, VALUE_HIGH);
        usleep(5000*1000);
    }else{
        gpio_export(60);
        gpio_set_direction(60, DIRECTION_OUTPUT);
        gpio_set_value(60, VALUE_HIGH);
        usleep(100*1000);
        gpio_set_value(60, VALUE_LOW);
        usleep(100*1000);
    }    
}


int main( int argc, char **argv )
{
    int i;
    bool ret;
    int retry = 0;
    pid_t fpid;
    struct sigaction sigact; /* SIGQUIT&SIGINT&SIGTERM signal handling */
    unsigned long f_size;
    float current = 0;
    char cmd[1024];
    char global_conf_file[128];
    char factory_conf_file[128];
    char result_file[128];
    char *gp_global = NULL;
    char *gp_factory = NULL;
    char *gp_result = NULL;
    char *factory_test_log_path = "/home/factory_test/test.log";
    int option_index = 0;
    static struct option long_options[] = {
        { "help", no_argument, 0, 0 },
        { "md134x", no_argument, 0, 0 },
        { "global", required_argument, 0, 0 },
        { "threhold", required_argument, 0, 0 },
        { "result", required_argument, 0, 0 },
        { 0,		  0,				 0, 0 }
    };
    
    printf("------RF module production test------\n");
    /* Parameter parsing */
    /* parse command line options */
    while ((i = getopt_long(argc, argv, "h", long_options, &option_index)) != -1) {
        switch (i) {
            case 'h':
                usage();
                return EXIT_FAILURE;                
                break;
            case 0:
                if (strcmp(long_options[option_index].name, "global") == 0) {
                    memset(global_conf_file, 0, sizeof(global_conf_file));
                    strcpy(global_conf_file, optarg);
                    gp_global = global_conf_file;
                    MSG("INFO: Global configuration file: %s\n", gp_global);
                }else if (strcmp(long_options[option_index].name, "threhold") == 0) {
                    memset(factory_conf_file, 0, sizeof(factory_conf_file));
                    strcpy(factory_conf_file, optarg);
                    gp_factory = factory_conf_file;
                    MSG("INFO: Threhold configuration file: %s\n", gp_factory);
                }else if (strcmp(long_options[option_index].name, "result") == 0) {
                    memset(result_file, 0, sizeof(result_file));
                    printf("result path: %s.\n", optarg);
                    strcpy(result_file, optarg);
                    gp_result = result_file;
                    MSG("INFO: Test result file: %s\n", gp_result);
                }else if (strcmp(long_options[option_index].name, "help") == 0) {
                    usage();
                    return EXIT_FAILURE;  
                }else if (strcmp(long_options[option_index].name, "md134x") == 0) {
                    use_md134x = true;
                    MSG("INFO: Use MD134X Module.\n");
                }
                break;
            default:
                MSG("ERROR: argument parsing\n");
                usage();
                return EXIT_FAILURE;
            }
    }

    pd_test_module_reset_init();
    atexit(exit_handler);
    pd_open_dll(use_md134x);
    
    if(use_md134x){
        int (*connect)(void *) = NULL;
        connect = pd_load_dll_function(use_md134x, "lgw_connect");
        if(connect == NULL){
            fprintf(stderr, "ERROR: Failed to get lgw_connect in dll.\n");
            pd_close_dll(use_md134x);
            exit(EXIT_FAILURE);
        }
        i = connect("/dev/ttyS4");
        if (i == LGW_REG_ERROR) {        
            fprintf(stderr, "ERROR: FAIL TO CONNECT BOARD ON /dev/ttyS4\n");
            pd_close_dll(use_md134x);
            exit(EXIT_FAILURE);    
        }
    }
    pd_load_configuration_files(gp_global, gp_factory);
    pd_input_product_id();

    /* Init current com  */
    i = hal_current_com_open(&current_com_target, ad_com_path_default);
    if(i == -1){
        fprintf(stderr, "Failed to open current ad com path \n");
        exit(EXIT_FAILURE);
    }
    
    printf("base board init starting. \n");
    radio_params_config(&g_tRfPara);
    radio_sx127x_init();
    printf("base board init end. \n");


    printf("test board init starting.\n");
    /* starting the concentrator */
    i = pd_lgw_start(use_md134x);
    if (i == LGW_HAL_SUCCESS) {
        printf("INFO: [main] concentrator started, packet can now be received. \n");
    } else {
        printf("ERROR: [main] failed to start the concentrator. \n");
        exit(EXIT_FAILURE);
    }
    printf("test board init end.\n");
    
    /* configure signal handling */
    sigemptyset(&sigact.sa_mask);
    sigact.sa_flags = 0;
    sigact.sa_handler = sig_handler;
    sigaction(SIGQUIT, &sigact, NULL); /* Ctrl-\ */
    sigaction(SIGINT, &sigact, NULL); /* Ctrl-C */
    sigaction(SIGTERM, &sigact, NULL); /* default "kill" command */      

    sem_init(&pdsem, 0, 0);
     
    while(!exit_sig && !quit_sig &&!test_done){ 
        switch(steps){
            case PD_TEST_IDLE:
            	memset(cmd, 0, sizeof(cmd));
            	sprintf(cmd + strlen(cmd), "echo -e \"");
            	sprintf(cmd + strlen(cmd), "\n***********************************\n");
            	sprintf(cmd + strlen(cmd), "`date`\n");
            	sprintf(cmd + strlen(cmd), "SX1301 ID: %s\n", pd_id_body);                
                last_steps = steps;
                steps = PD_TEST_RADIO;
                sleep(1);
                break;
            case PD_TEST_RADIO:
                fprintf(stdout, "INFO: Radio 0 testing, please wait...\n");
                retry = 0;
                do{
                    i = pd_test_radio(0);
                    retry++;
                    usleep(10*1000);
                }while(i < 0 && retry < 3);
                
                if(0 == i){
                    printf("radio 0 test success. \n");
                }else{
                    printf("radio 0 test fail. \n");
                }
                usleep(100*1000);
                fprintf(stdout, "INFO: Radio 1 testing, please wait...\n");
                retry = 0;
                do{
                    i = pd_test_radio(1);
                    retry++;
                    usleep(10*1000);
                }while(i < 0 && retry < 3);
                
                if(0 == i){
                    printf("radio 1 test success. \n");
                }else{
                    printf("radio 1 test fail. \n");
                }
                steps = PD_TEST_RADIO_DONE;
                break;
            case PD_TEST_RADIO_DONE:
                sprintf(cmd + strlen(cmd), "sx1301  radio_0 test %s \n", (true == result.radio0_result)?"success":"failed");
                sprintf(cmd + strlen(cmd), "sx1301  radio_1 test %s \n", (true == result.radio1_result)?"success":"failed");
                usleep(100*1000);
                steps = PD_TEST_RECV_START;
                break;
            case PD_TEST_RECV_START:
                retry = 0;
                pd_test_recv_start();
                sprintf(cmd + strlen(cmd), "sx1301 0 TX Frequency: %uHZ\n", rx_test_freq[0]);
                last_steps = steps;
                steps = PD_TEST_CURRENT;
                break;
            case PD_TEST_RECV_DONE:
                if(0 >  pd_test_recv_wait()){
                    retry++;
                    if(retry < 3){
                        pd_test_recv_start();
                        last_steps = PD_TEST_RECV_START;
                        steps = PD_TEST_CURRENT;
                        break;
                    }
                }               
                sleep(1);
                
    			sprintf(cmd + strlen(cmd), "Receive    rssi: %.2fdBm		snr: %.2fdB		current: %.2fmA\n", (float)result.recv_rssi, (float)result.recv_snr, result.recv_current);
    			if ((float)result.recv_rssi < receive_rssi_min) {
    				result.recv_result = false;
    				sprintf(cmd + strlen(cmd), "Receive rssi value is abnormal, test value is %.2fdBm, minimum value is %.2fdBm\n", 
    					(float)result.recv_rssi, receive_rssi_min);
    			}
    			if ((float)result.recv_snr < receive_snr_min) {
    				result.recv_result = false;
    				sprintf(cmd + strlen(cmd), "Receive snr value is abnormal, test value is %.2fdB, minimum value is %.2fdB\n", 
    					(float)result.recv_snr, receive_snr_min);
    			}
				if (result.recv_current < receive_current_min || result.recv_current > receive_current_max) {
                    result.recv_result = false;
					sprintf(cmd + strlen(cmd), "Receive current value is abnormal, test value is %.2fmA, minimum value is %.2fmA, maximum value is %.2fmA\n", 
						result.recv_current, receive_current_min, receive_current_max);
				} 
                
                steps = PD_TEST_SEND_START;
                break;
            case PD_TEST_SEND_START:
                retry = 0;
                pd_test_send_start();
                sprintf(cmd + strlen(cmd), "sx1301 1 TX Frequency: %uHZ\n", tx_test_freq[0]);
                last_steps = steps;
                steps = PD_TEST_CURRENT;
                break;
            case PD_TEST_SEND_DONE:
                //pd_test_send_wait();
                if(0 >  pd_test_send_wait()){
                    retry++;
                    if(retry < 3){
                        pd_test_send_start();
                        last_steps = PD_TEST_SEND_START;
                        steps = PD_TEST_CURRENT;
                        break;
                    }
                }                 
                sleep(1);
                
    			sprintf(cmd + strlen(cmd), "Send    rssi: %.2fdBm		snr: %.2fdB		current: %.2fmA\n", (float)result.send_rssi, (float)result.send_snr, result.send_current);
    			if ((float)result.send_rssi < send_rssi_min) {
    				result.send_result = false;
    				sprintf(cmd + strlen(cmd), "Send rssi value is abnormal, test value is %.2fdBm, minimum value is %.2fdBm\n", 
    					(float)result.send_rssi, send_rssi_min);
    			}
    			if ((float)result.send_snr < send_snr_min) {
    				result.send_result = false;
    				sprintf(cmd + strlen(cmd), "Send snr value is abnormal, test value is %.2fdB, minimum value is %.2fdB\n", 
    					(float)result.send_snr, send_snr_min);
    			}
				if (result.send_current < send_current_min || result.send_current > send_current_max) {
                    result.send_result = false;
					sprintf(cmd + strlen(cmd), "Send current value is abnormal, test value is %.2fmA, minimum value is %.2fmA, maximum value is %.2fmA\n", 
						result.send_current, send_current_min, send_current_max);
				}
                 
                steps = PD_TEST_DONE;
                break;
            case PD_TEST_CURRENT:
                usleep(time_on_air*1000/4);
                pd_test_current(&current);
                printf("sx1301 current test.\n");
                if(last_steps == PD_TEST_RECV_START){
                    result.recv_current = current;
                    steps = PD_TEST_RECV_DONE;
                }else if(last_steps == PD_TEST_SEND_START){
                    result.send_current = current;                   
                    steps = PD_TEST_SEND_DONE;
                }
                break;          
            case PD_TEST_DONE:
                hal_current_com_close(current_com_target);
                pd_lgw_stop(use_md134x);
                pd_close_dll(use_md134x);
                test_done = true;
                printf("radio0 : %d .\n", result.radio0_freq);
                printf("radio1 : %d .\n", result.radio1_freq);
                printf("recv freq : %d .\n", result.recv_freq);
                printf("recv current : %.1f .\n", result.recv_current);
                printf("recv rssi : %i .\n", result.recv_rssi);
                printf("recv snr : %i .\n", result.recv_snr);
                printf("send freq : %d .\n", result.send_freq);
                printf("send current : %.1f .\n", result.send_current);
                printf("send rssi : %i .\n", result.send_rssi);
                printf("send snr : %i .\n", result.send_snr);
                printf("radio0 result : %d .\n", result.radio0_result);
                printf("radio1 result : %d .\n", result.radio1_result);
                printf("recv result : %d .\n", result.recv_result);
                printf("send result : %d .\n", result.send_result);
            	if (result.radio0_result && result.radio1_result && result.recv_result && result.send_result) {
            		sprintf(cmd + strlen(cmd), "\33[32mPASS\33[0m");
            	} else {
            		//fprintf(stdout, "Please read %s for more information\n", factory_test_log_path);
            		sprintf(cmd + strlen(cmd), "\033[31mFAILED\033[0m");
            	}

            	sprintf(cmd + strlen(cmd), "\"");
                fpid = fork();
                if(fpid < 0){
                    printf("fork error.\n");
                }else if(fpid == 0){
                    execl("/bin/bash", "bash", "-c", cmd, (char *)0);
                    exit(0);
                }else{
                    waitpid(fpid, NULL, 0);
                    //pd_test_result_log_file((gp_result == NULL) ? factory_test_log_path : gp_result);
                	sprintf(cmd + strlen(cmd), " >> %s", (gp_result == NULL) ? factory_test_log_path : gp_result);
                    printf("------RF module production test done------\n");
                    execl("/bin/bash", "bash", "-c", cmd, (char *)0);
                }
                break;
            default:
                break;
        }
    
    }
#ifdef __GNUC__
    return 0;
#endif
}



int pd_test_radio(int radio)
{
    int i;
    int radio_idx = radio;
    char contents[]="easylinkin-test-radio";
    char buf[128];
    struct timespec ts;

    if(radio == 0){
        result.radio0_freq = radio_freq[radio];
    }else if(radio == 1){
        result.radio1_freq = radio_freq[radio];
    }else{
        printf("radio test number error.\n");
        return -1;
    }
    /* init */
    i = sem_init(&sem_radio[radio_idx], 0, 0);
    if (i != 0){
        printf("semaphore sem_radio[%d] initialization failed. \n", radio_idx);
    }    
    i = pthread_create(&pthread_radios, NULL, (void*)pd_test_radios_recv_thread, &radio_idx);
    if (i != 0){
        printf("pthread pthread_radios create failed. \n");
    }
    usleep(100*1000);
    /* prepare */
    memcpy(buf, contents, strlen(contents));
    memcpy(&buf[strlen(contents)], pd_id_body, PD_TOTAL_LEN);
    buf[strlen(contents)+ PD_TOTAL_LEN] = 0x30 + radio_idx;
    /* send */
    radio_sx127x_send(radio_freq[radio], buf, strlen(contents) + PD_TOTAL_LEN + 1);
    /* wait receive finish */
	clock_gettime( CLOCK_REALTIME, &ts );
	ts.tv_sec += 3;
	if((i = sem_timedwait(&sem_radio[radio_idx], &ts)) == -1 && errno == EINTR){	
		//sprintf(cmd + strlen(cmd), "sx1301  radio_%d test failed \n", rf);
		//test_pass = false;
		pthread_cancel(pthread_radios);
        pthread_join(pthread_radios, NULL);
        if(radio == 0){
            result.radio0_result = false;
        }else{
            result.radio1_result = false;
        }
		return -1;
	}

	if (i == -1) {
		if (errno == ETIMEDOUT)
			printf("sem_timedwait() timed out\n");
		else
			perror("sem_timedwait ");
		//test_pass = false;
		//sprintf(cmd + strlen(cmd), "sx1301  radio_%d test failed \n", rf);
		pthread_cancel(pthread_radios);
        pthread_join(pthread_radios, NULL);
        if(radio == 0){
            result.radio0_result = false;
        }else{
            result.radio1_result = false;
        }        
		return -1;
	} else{
		printf("sem_timedwait() succeeded\n");
		//sprintf(cmd + strlen(cmd), "sx1301  radio_%d test success \n", rf);
	}
        
    //memcpy(&buf[strlen(contents)], pd_id_body, PD_TOTAL_LEN);
    //radio_sx127x_send(0, buf, strlen(contents)+ PD_TOTAL_LEN);
    //radio_sx127x_send(radio_freq[radio], buf, strlen(contents)+ PD_TOTAL_LEN);
    pthread_join(pthread_radios, NULL);
    if(radio == 0){
        result.radio0_result = true;
    }else{
        result.radio1_result = true;
    }    
    return 0;
}


static void pd_test_module_recv_thread(void *arg)
{
    int i;
    int index;
    /* clock and log rotation management */
    int time_check = 0; /* variable used to limit the number of calls to time() function */

    /* allocate memory for packet fetching and processing */
    struct lgw_pkt_rx_s rxpkt[16]; /* array containing up to 16 inbound packets metadata */
    struct lgw_pkt_rx_s *p; /* pointer on a RX packet */
    int nb_pkt;

    /* local timestamp variables until we get accurate GPS time */
    struct timespec fetch_time;
    char fetch_timestamp[30];
    struct tm * x;
    char contents[]="easylinkin-module-test-recv";


    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
    printf("pd_test_module_recv_thread entry.\n");
    /* main loop */
    while ((quit_sig != 1) && (exit_sig != 1)) {
        /* fetch packets */
        memset(rxpkt, 0, sizeof(rxpkt));
        nb_pkt = pd_lgw_receive(use_md134x, ARRAY_SIZE(rxpkt), rxpkt);
        if (nb_pkt == LGW_HAL_ERROR) {
            MSG("ERROR: failed packet fetch, exiting\n");
            return;
        } else if (nb_pkt == 0) {
            /* wait a short time if no packets */
            usleep(1000*FETCH_SLEEP_MS);
        } else {
            /* local timestamp generation until we get accurate GPS time */
            clock_gettime(CLOCK_REALTIME, &fetch_time);
            x = gmtime(&(fetch_time.tv_sec));
            sprintf(fetch_timestamp, "%04i-%02i-%02i %02i:%02i:%02i.%03liZ", (x->tm_year) + 1900, (x->tm_mon) + 1, x->tm_mday, x->tm_hour, x->tm_min, x->tm_sec, (fetch_time.tv_nsec) / 1000000); /* ISO 8601 format */
        }
        
        /* log packets */
        for (i = 0; i < nb_pkt; ++i) {
            p = &rxpkt[i];
            /* printf received package into stdout */
			if(p->status == STAT_CRC_OK){
				if(!memcmp(p->payload , contents, strlen(contents))){
                    if(!memcmp(&p->payload[strlen(contents)] , pd_id_body, PD_TOTAL_LEN)){
                        printf("----INFO: Test Module Received package : %s .\n", p->payload);
                        DEBUG_ARRAY(index, p->size, p->payload);
                        result.recv_rssi = (int16_t)p->rssi;
                        result.recv_snr = (int8_t)p->snr;
                        result.recv_result = true;
                        if(((int8_t)p->snr < 0) || (int16_t)p->rssi < receive_rssi_min){
                            continue;
                        }
                        sem_post(&sem_recv);
                        pthread_exit(0);
                    }
				}
			}            
        }

        /* check time and rotate log file if necessary */
    }
}


int pd_test_recv_start(void)
{
    int i;
    char buf[128];    
    char contents[]="easylinkin-module-test-recv";
    struct lgw_pkt_tx_s txpkt;
    
    /* init */
    i = sem_init(&sem_recv, 0, 0);
    if (i != 0){
        printf("semaphore sem_recv initialization failed. \n");
        return -1;
    }    
    i = pthread_create(&pthread_module_recv, NULL, (void*)pd_test_module_recv_thread, NULL);
    if (i != 0){
        printf("pthread pthread_module_recv create failed. \n");
        return -1;
    }
    usleep(100*1000);
    /* prepare */
    memset(&txpkt, 0, sizeof(txpkt));
	txpkt.tx_mode = IMMEDIATE;	
	txpkt.rf_chain = 0;	 
	txpkt.rf_power = 20;	
	txpkt.modulation = MOD_LORA;
	txpkt.bandwidth = BW_125KHZ;
	txpkt.datarate = DR_LORA_SF12;
	txpkt.coderate = CR_LORA_4_5;
	txpkt.invert_pol = false;//true;    
	txpkt.preamble = 8;    
	txpkt.size = strlen(contents) + PD_TOTAL_LEN;
    txpkt.freq_hz = rx_test_freq[0];
#if 0    
#ifdef MD134X
    time_on_air = lgw_time_on_air(&txpkt);
#else
    time_on_air = lgw_time_on_air(&txpkt, false);
#endif
#else
    time_on_air = pd_get_time_on_air(use_md134x, &txpkt);
#endif
    printf("time-on-air: %d .\n", time_on_air);
    g_tRfPara.cPwr = txpkt.rf_power;
    g_tRfPara.ucSf = 12;
    radio_params_config(&g_tRfPara);
    memcpy(buf, contents, strlen(contents));
    memcpy(&buf[strlen(contents)], pd_id_body, PD_TOTAL_LEN);
    //time_on_air = radio_sx127x_get_time_on_air(strlen(contents) + PD_TOTAL_LEN)/1000;
    /* send */
    result.recv_freq = rx_test_freq[0];
    radio_sx127x_send(rx_test_freq[0], buf, strlen(contents) + PD_TOTAL_LEN);
    fprintf(stdout, "Receive testing, please wait...\n");
    
    return 0;
}

int pd_test_recv_wait(void)
{
    int i;    
    struct timespec ts;
    
    /* wait receive finish */
	clock_gettime( CLOCK_REALTIME, &ts );
	ts.tv_sec += 5;
	if((i = sem_timedwait(&sem_recv, &ts)) == -1 && errno == EINTR){	
		//sprintf(cmd + strlen(cmd), "sx1301  radio_%d test failed \n", rf);
		//test_pass = false;
		pthread_cancel(pthread_module_recv);
        pthread_join(pthread_module_recv, NULL);
		return -1;
	}

	if (i == -1) {
		if (errno == ETIMEDOUT)
			printf("sem_timedwait() timed out\n");
		else
			perror("sem_timedwait");
		//test_pass = false;
		//sprintf(cmd + strlen(cmd), "sx1301  radio_%d test failed \n", rf);
		pthread_cancel(pthread_module_recv);
        pthread_join(pthread_module_recv, NULL);
		return -1;
	} else{
		printf("sem_timedwait() succeeded\n");
		//sprintf(cmd + strlen(cmd), "sx1301  radio_%d test success \n", rf);
	}
        
    //memcpy(&buf[strlen(contents)], pd_id_body, PD_TOTAL_LEN);
    //radio_sx127x_send(0, buf, strlen(contents)+ PD_TOTAL_LEN);
    //radio_sx127x_send(radio_freq[radio], buf, strlen(contents)+ PD_TOTAL_LEN);
    pthread_join(pthread_module_recv, NULL);

    return 0;

}



static void pd_standard_module_recv_thread(void *arg)
{
    int i;
    int index;
    /* clock and log rotation management */
    int time_check = 0; /* variable used to limit the number of calls to time() function */

    /* allocate memory for packet fetching and processing */
    rfRxPkt_t rxpkt; /* array containing up to 16 inbound packets metadata */
    rfRxPkt_t *p; /* pointer on a RX packet */
    int nb_pkt;

    /* local timestamp variables until we get accurate GPS time */
    struct timespec fetch_time;
    char fetch_timestamp[30];
    struct tm * x;
    char contents[]="easylinkin-module-test-send";

    //int radio_sx127x_receive(rfRxPkt *pkt);
    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
    printf("pd_standard_module_recv_thread entry.\n");
    /* main loop */
    while ((quit_sig != 1) && (exit_sig != 1)) {
        /* fetch packets */
        memset(&rxpkt, 0, sizeof(rxpkt));
        nb_pkt = radio_sx127x_receive(&rxpkt);
        if (nb_pkt < 0) {
            MSG("ERROR: failed packet fetch, exiting\n");
            return;
        } else {
            /* local timestamp generation until we get accurate GPS time */
            clock_gettime(CLOCK_REALTIME, &fetch_time);
            x = gmtime(&(fetch_time.tv_sec));
            sprintf(fetch_timestamp, "%04i-%02i-%02i %02i:%02i:%02i.%03liZ", (x->tm_year) + 1900, (x->tm_mon) + 1, x->tm_mday, x->tm_hour, x->tm_min, x->tm_sec, (fetch_time.tv_nsec) / 1000000); /* ISO 8601 format */
        }
        
        p = &rxpkt;
        /* printf received package into stdout */
		if(!memcmp(p->payload , contents, strlen(contents))){
            if(!memcmp(&p->payload[strlen(contents)] , pd_id_body, PD_TOTAL_LEN)){
                printf("----INFO: Standard Module Received package : %s .\n", p->payload);
                DEBUG_ARRAY(index, p->size, p->payload);
                result.send_rssi = p->rssi;
                result.send_snr = p->snr;
                result.send_result = true;
                if(((int8_t)p->snr < 0) || (int16_t)p->rssi < send_rssi_min){
                    continue;
                }
                sem_post(&sem_send);
                pthread_exit(0);
            }
		}  

        /* check time and rotate log file if necessary */
    }
}

int pd_test_send_start(void)
{
    int i;
    int index;
    char buf[128];    
    char contents[]="easylinkin-module-test-send";
    struct timespec ts;
    struct lgw_pkt_tx_s txpkt;

	memset(&txpkt, 0, sizeof(txpkt));	 
	txpkt.tx_mode = IMMEDIATE;	
	txpkt.rf_chain = 0;	 
	txpkt.rf_power = g_tRfPara.cPwr;	
	txpkt.modulation = MOD_LORA;
	txpkt.bandwidth = BW_125KHZ;
	txpkt.datarate = DR_LORA_SF12;
	txpkt.coderate = CR_LORA_4_5;
	txpkt.invert_pol = true;
	txpkt.no_crc = true;	
    txpkt.no_header = false;
	txpkt.preamble = 8;
	txpkt.size = strlen(contents) + PD_TOTAL_LEN;
    txpkt.freq_hz = tx_test_freq[0];
#if 0    
#ifdef MD134X
    time_on_air = lgw_time_on_air(&txpkt);
#else
    time_on_air = lgw_time_on_air(&txpkt, false);
#endif
#else
    time_on_air = pd_get_time_on_air(use_md134x, &txpkt);
#endif
    result.send_freq = tx_test_freq[0];

    g_tRfPara.ulFreq = tx_test_freq[0];
    g_tRfPara.cPwr = txpkt.rf_power;
    g_tRfPara.ucSf = 12;
    radio_params_config(&g_tRfPara);
    radio_sx127x_start_rx();
    /* prepare */
    memcpy(buf, contents, strlen(contents));
    memcpy(&buf[strlen(contents)], pd_id_body, PD_TOTAL_LEN);
    memcpy(txpkt.payload, buf, strlen(contents) + PD_TOTAL_LEN);
    /* init */
    i = sem_init(&sem_send, 0, 0);
    if (i != 0){
        printf("semaphore sem_send initialization failed. \n");
        return -1;
    }    
    i = pthread_create(&pthread_standard_recv, NULL, (void*)pd_standard_module_recv_thread, NULL);
    if (i != 0){
        printf("pthread pthread_standard_recv create failed. \n");
        return -1;
    }
    usleep(100*1000); 
    /* send */
	i = pd_lgw_send(use_md134x, txpkt); 
	if (i != LGW_HAL_SUCCESS) {
        fprintf(stderr, "Test failed\n");
        return -1;
	} else	{
        fprintf(stdout, "Send testing, please wait...\n");
	}
    for(index = 0; index < txpkt.size; index++){
        printf("%02x ", txpkt.payload[index]);
    }
    printf("\n");
    return 0;
}

int pd_test_send_wait(void)
{
    int i;
    struct timespec ts;
    
    /* wait receive finish */
	clock_gettime( CLOCK_REALTIME, &ts );
	ts.tv_sec += 5;
	if((i = sem_timedwait(&sem_send, &ts)) == -1 && errno == EINTR){	
		//sprintf(cmd + strlen(cmd), "sx1301  radio_%d test failed \n", rf);
		//test_pass = false;
		pthread_cancel(pthread_standard_recv);
        pthread_join(pthread_standard_recv, NULL);
		return -1;
	}

	if (i == -1) {
		if (errno == ETIMEDOUT)
			printf("sem_timedwait() timed out\n");
		else
			perror("sem_timedwait");
		//test_pass = false;
		//sprintf(cmd + strlen(cmd), "sx1301  radio_%d test failed \n", rf);
		pthread_cancel(pthread_standard_recv);
        pthread_join(pthread_standard_recv, NULL);
		return -1;
	} else{
		printf("sem_timedwait() succeeded\n");
		//sprintf(cmd + strlen(cmd), "sx1301  radio_%d test success \n", rf);
	}
        
    //memcpy(&buf[strlen(contents)], pd_id_body, PD_TOTAL_LEN);
    //radio_sx127x_send(0, buf, strlen(contents)+ PD_TOTAL_LEN);
    //radio_sx127x_send(radio_freq[radio], buf, strlen(contents)+ PD_TOTAL_LEN);
    pthread_join(pthread_standard_recv, NULL);

    return 0;

}



int pd_test_result_print(int fd)
{
    return 0;
}









